# -*- coding: utf-8 -*-
"""
@Time ： 2025/5/25 10:59
@Auth ： 九问
@File ：assert_utils.py
@IDE ：PyCharm
@Email : 2750826557@qq.com
"""
import logging
from typing import Union, List, Dict, Any

logger = logging.getLogger("assert")


def assert_json(response_data: dict, validate_rules: Union[dict, list]):
    """
    支持YAML格式的JSON断言工具，支持两种格式：
    1. 列表格式 [{"eq": ["code", 0]}, {"contains": ["message", "成功"]}]
    2. 字典格式 {"code": 0, "message": "success", "$.data.id": 100}
    
    :param response_data: API返回的JSON数据
    :param validate_rules: 从YAML转换的断言规则
    """
    logger.info("🔍 开始执行响应数据验证")
    
    # 处理字典格式
    if isinstance(validate_rules, dict):
        for field_path, expected in validate_rules.items():
            try:
                if field_path.startswith('$.'):
                    # 如果是 jsonpath 格式，去掉开头的 '$.'
                    field_path = field_path[2:]
                actual = _get_nested_value(response_data, field_path)
                logger.debug(f"验证项: eq | 字段: {field_path} | 期望: {expected} | 实际: {actual}")
                assert actual == expected, f"【值相等验证失败】字段 {field_path} 应为 {expected}，实际为 {actual}"
                logger.info(f"✔ 验证通过：{field_path} 的值等于 {expected}")
            except (KeyError, IndexError):
                raise AssertionError(f"字段 {field_path} 不存在于响应数据中")
        
    # 处理列表格式
    elif isinstance(validate_rules, list):
        for rule in validate_rules:
            for check_type, checks in rule.items():
                field_path, expected = checks
                # 如果是 jsonpath 格式，去掉开头的 '$.'
                if isinstance(field_path, str) and field_path.startswith('$.'):
                    field_path = field_path[2:]
                
                try:
                    actual = _get_nested_value(response_data, field_path)
                except (KeyError, IndexError):
                    raise AssertionError(f"字段 {field_path} 不存在于响应数据中")
                
                logger.debug(f"验证项: {check_type} | 字段: {field_path} | 期望: {expected} | 实际: {actual}")

                if check_type == "eq":
                    assert actual == expected, f"【值相等验证失败】字段 {field_path} 应为 {expected}，实际为 {actual}"
                    logger.info(f"✔ 验证通过：{field_path} 的值等于 {expected}")

                elif check_type == "neq":
                    assert actual != expected, f"【值不等验证失败】字段 {field_path} 不应为 {actual}"
                    logger.info(f"✔ 验证通过：{field_path} 的值不等于 {expected}")

                elif check_type == "in":
                    assert actual in expected, f"【包含验证失败】字段 {field_path} 的值 {actual} 不在允许值 {expected} 中"
                    logger.info(f"✔ 验证通过：{field_path} 的值 {actual} 在允许列表 {expected} 中")

                elif check_type == "not_in":
                    assert actual not in expected, f"【不包含验证失败】字段 {field_path} 的值 {actual} 不应出现在 {expected} 中"
                    logger.info(f"✔ 验证通过：{field_path} 的值 {actual} 不在排除列表 {expected} 中")

                elif check_type == "contains":
                    assert expected in str(
                        actual), f"【文本包含验证失败】字段 {field_path} 的文本 '{actual}' 应包含 '{expected}'"
                    logger.info(f"✔ 验证通过：{field_path} 的文本包含 '{expected}'")
                
                elif check_type == "greater_than" or check_type == "gt":
                    assert actual > expected, f"【大于验证失败】字段 {field_path} 的值 {actual} 应大于 {expected}"
                    logger.info(f"✔ 验证通过：{field_path} 的值 {actual} 大于 {expected}")
                
                elif check_type == "less_than" or check_type == "lt":
                    assert actual < expected, f"【小于验证失败】字段 {field_path} 的值 {actual} 应小于 {expected}"
                    logger.info(f"✔ 验证通过：{field_path} 的值 {actual} 小于 {expected}")
                
                else:
                    raise ValueError(f"不支持的验证类型: {check_type}")
    
    else:
        raise TypeError("断言规则必须是字典或列表类型")

    logger.info("🎉 所有验证项通过")


def _get_nested_value(data: Union[Dict, List], path: str) -> Any:
    """支持点分路径获取嵌套值"""
    keys = path.split('.')
    value = data
    for key in keys:
        if isinstance(value, list) and key.isdigit():
            value = value[int(key)]
        elif isinstance(value, dict) and key in value:
            value = value[key]
        else:
            raise KeyError(f"无法在响应数据中找到键 '{key}'")
    return value


# 测试用例
if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    # 测试数据
    test_data = {
        "code": 0,
        "message": "订单创建成功",
        "user": {
            "id": "usr_123",
            "name": "张三",
            "tags": "VIP",
            "profile": {
                "level": 3,
                "bio": "高级软件工程师"
            }
        }
    }

    # 列表格式验证规则
    list_rules = [
        {"eq": ["code", 0]},
        {"contains": ["message", "成功"]},
        {"in": ["user.tags", ["VIP", "SVIP"]]},
        {"not_in": ["user.profile.level", [1, 2]]},
        {"contains": ["user.profile.bio", "工程师"]}
    ]
    
    # 字典格式验证规则
    dict_rules = {
        "code": 0,
        "message": "订单创建成功",
        "user.tags": "VIP",
        "user.profile.level": 3
    }

    print("测试列表格式断言:")
    assert_json(test_data, list_rules)
    
    print("\n测试字典格式断言:")
    assert_json(test_data, dict_rules)
